#ifndef LRU_HASH_H_
#define LRU_HASH_H_
#include "list.h"
#include <ext/hash_map>
using namespace std;
using namespace __gnu_cxx;

namespace demon_tool
{
	template<typename K, typename V>
	struct LruItem
	{
		struct list_node lnode;
		K key;
		V item;
	};

	class default_free
	{
		public:
			void operator()(void*)
			{
				return;
			}
	};
	
	template<typename K, typename V, typename hash, typename equal, typename freefunc = default_free>
	class LruHash
	{
		typedef LruItem<K, V> val_type;
		typedef hash_map<K, val_type*, hash, equal>	hmap;
		typedef typename hmap::iterator	hiter;
		public:
			LruHash()
			{
				LIST_INIT(&(head_));
			}
			int count() const{return map_.size();}

			int last(K* key)
			{
				if(!LIST_EMPTY(&head_))
				{
					struct list_node* tail = head_.prev;
					val_type* item = container_of(tail, val_type, lnode);
					*key = item->key;
					return 0;
				}
				return -1;
			}

			int find(K key, val_type** v)
			{
				hiter pos = map_.find(key);
				if(map_.end() != pos)
				{
					*v = pos->second;
					val_type* res = *v;
					LIST_DEL(&(res->lnode));
					LIST_INSERT_AFTER(&(head_), &(res->lnode));
					return 0;
				}
				return -1;
			}
			
			int insert(K key, val_type* v)
			{
				hiter pos = map_.find(key);
				if(map_.end() == pos)
				{
					v->key = key;
					map_.insert(make_pair(key, v));
					LIST_INSERT_AFTER(&head_, &(v->lnode));
				}
				else
				{
					return -1;
				}
				return 0;
			}

			int set(K key, val_type* v)
			{
				hiter pos = map_.find(key);
				if(map_.end() != pos)
				{
					erase(key);
				}
				return insert(key, v);
			}
			
			int erase(K key)
			{
				hiter pos = map_.find(key);
				if(map_.end() != pos)
				{
					val_type* v = pos->second;
					LIST_DEL(&(v->lnode));
					map_.erase(pos);
					freefunc f;
					f(v);
					return 0;
				}
				else
				{
					return -1;
				}
			}
			
		private:
			struct list_node head_;
			hmap map_;
	};
}
#endif
